1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.systems.input; 12 import hip.util.data_structures; 13 import hip.error.handler; 14 15 version(WebAssembly) version = QueuePopulatedExternally; 16 else version(UWP) version = QueuePopulatedExternally; 17 else version(PSVita) version = QueuePopulatedExternally; 18 else version(AppleOS) version = QueuePopulatedExternally; 19 20 version(Android) 21 { 22 import hip.jni.helper.androidlog; 23 import hip.jni.jni; 24 import hip.jni.helper.jnicall; 25 26 ///Setups an Android Package for HipremeEngine 27 alias HipAndroidInput = javaGetPackage!("com.hipremeengine.app.HipInput"); 28 alias HipAndroidRenderer = javaGetPackage!("com.hipremeengine.app.Hip_GLES30_Renderer"); 29 30 @JavaFunc!(HipAndroidInput) void onMotionEventActionMove(int pointerId, float x, float y) 31 { 32 HipEventQueue.post(0, HipEventQueue.EventType.touchMove, HipEventQueue.Touch(cast(ushort)pointerId, x,y)); 33 } 34 35 @JavaFunc!(HipAndroidInput) void onMotionEventActionPointerDown(int pointerId, float x, float y) 36 { 37 HipEventQueue.post(0, HipEventQueue.EventType.touchDown, HipEventQueue.Touch(cast(ushort)pointerId, x,y)); 38 } 39 @JavaFunc!(HipAndroidInput) void onMotionEventActionPointerUp(int pointerId, float x, float y) 40 { 41 HipEventQueue.post(0, HipEventQueue.EventType.touchUp, HipEventQueue.Touch(cast(ushort)pointerId, x,y)); 42 } 43 @JavaFunc!(HipAndroidInput) void onMotionEventActionScroll(float x, float y) 44 { 45 HipEventQueue.post(0, HipEventQueue.EventType.touchScroll, HipEventQueue.Scroll(x,x,0)); 46 } 47 48 @JavaFunc!(HipAndroidRenderer) void onRendererResize(int x, int y) 49 { 50 ///Must be executed on the render thread :( 51 import hip.hiprenderer; 52 import hip.graphics.g2d.renderer2d; 53 HipRenderer.setWindowSize(x, y); 54 resizeRenderer2D(cast(uint)x, cast(uint)y); 55 // HipEventQueue.post(0, HipEventQueue.EventType.windowResize, HipEventQueue.Resize(cast(uint)x, cast(uint)y)); 56 } 57 58 mixin javaGenerateModuleMethodsForPackage!(HipAndroidInput, hip.systems.input, false); 59 mixin javaGenerateModuleMethodsForPackage!(HipAndroidRenderer, hip.systems.input, false); 60 } 61 else version(QueuePopulatedExternally) 62 { 63 export extern(System) 64 { 65 void HipInputOnTouchPressed(uint id, float x, float y) 66 { 67 HipEventQueue.post(0, HipEventQueue.EventType.touchDown, HipEventQueue.Touch(cast(ushort)id, x, y)); 68 } 69 void HipInputOnTouchMoved(uint id, float x, float y) 70 { 71 HipEventQueue.post(0, HipEventQueue.EventType.touchMove, HipEventQueue.Touch(cast(ushort)id, x, y)); 72 } 73 void HipInputOnTouchReleased(uint id, float x, float y) 74 { 75 HipEventQueue.post(0, HipEventQueue.EventType.touchUp, HipEventQueue.Touch(cast(ushort)id, x, y)); 76 } 77 void HipInputOnTouchScroll(float x, float y, float z) 78 { 79 HipEventQueue.post(0, HipEventQueue.EventType.touchScroll, HipEventQueue.Scroll(x,y,z)); 80 } 81 void HipInputOnKeyDown(uint virtualKey) 82 { 83 import hip.event.dispatcher; 84 HipEventQueue.post(0, HipEventQueue.EventType.keyDown, HipEventQueue.Key(cast(ushort)virtualKey.getHipKeyFromSystem)); 85 } 86 void HipInputOnKeyUp(uint virtualKey) 87 { 88 import hip.event.dispatcher; 89 HipEventQueue.post(0, HipEventQueue.EventType.keyUp, HipEventQueue.Key(cast(ushort)virtualKey.getHipKeyFromSystem)); 90 } 91 void HipInputOnGamepadConnected(ubyte id, ubyte type) 92 { 93 HipEventQueue.post(0, HipEventQueue.EventType.gamepadConnected, HipEventQueue.Gamepad(id, type)); 94 } 95 void HipInputOnGamepadDisconnected(ubyte id, ubyte type) 96 { 97 HipEventQueue.post(0, HipEventQueue.EventType.gamepadDisconnected, HipEventQueue.Gamepad(id, type)); 98 } 99 void HipOnRendererResize(int x, int y) 100 { 101 HipEventQueue.post(0, HipEventQueue.EventType.windowResize, HipEventQueue.Resize(cast(uint)x, cast(uint)y)); 102 } 103 } 104 } 105 106 /** 107 * High efficient(at least memory-wise), tightly packed Input queue that supports any kind of data in 108 * a single allocated memory pool(no fragmentation). 109 * 110 * The input queue is populated by external APIs, like UWP's CoreWindow, Android's app and SDL2 Event. 111 * This way, it is possible to create a centralized input resource. This class does not creates the entire 112 * input system. It creates its base for being handled. 113 */ 114 class HipEventQueue : EventQueue 115 { 116 enum EventType : ubyte 117 { 118 ///Mouse was basically ignored for the sake of making it only touch ( it should be easier ) 119 touchDown, 120 touchMove, 121 touchUp, 122 touchScroll, 123 keyDown, 124 keyUp, 125 126 gamepadConnected, 127 gamepadDisconnected, 128 129 ///When user returns to application 130 focusReceived, 131 ///When user exists the application 132 focusLost, 133 windowResize, 134 exit 135 } 136 137 struct InputEvent 138 { 139 EventType type; 140 ubyte evSize; 141 void[0] evData; 142 pragma(inline, true) 143 T get(T)(){return *(cast(T*)evData);} 144 } 145 146 struct Key 147 { 148 ushort id; 149 } 150 151 struct Touch 152 { 153 ///Ubyte unnecessary, as I doubt any arch would receive a struct non divisible by 2 154 ushort id; 155 float xPos; 156 float yPos; 157 } 158 struct Resize 159 { 160 uint width; 161 uint height; 162 } 163 164 struct Gamepad 165 { 166 ubyte id; 167 ///See hip.systems.gamepad.HipGamepadTypes 168 ubyte type; 169 } 170 171 struct Scroll 172 { 173 float x, y,z; 174 } 175 176 ///This class should probably not contain more than one instance(unless 2 people are playing) 177 protected __gshared HipEventQueue[] controllers; 178 179 protected this(uint touchStructsCapacity = 126) 180 { 181 ///Uses capacity*greatest structure size 182 super(cast(uint)Touch.sizeof*touchStructsCapacity); 183 } 184 InputEvent* poll(){return cast(InputEvent*)super.poll();} 185 186 static HipEventQueue newController(uint touchStructsCapacity = 126) 187 { 188 HipEventQueue ip = new HipEventQueue(touchStructsCapacity); 189 controllers~= ip; 190 return ip; 191 } 192 193 /** External API used for getting the input events inside an internal queue. This way the API can remains the same*/ 194 static void post(T)(uint id, EventType type, T ev) 195 { 196 import hip.util.format; 197 if(id >= controllers.length) 198 { 199 ErrorHandler.assertExit(false, format!("Input controller out of range!(ID: %s, Type: %s)")(id, type)); 200 } 201 controllers[id].post(cast(ubyte)type, ev); 202 } 203 204 /** External API used for getting the input events inside an internal queue. This way the API can remains the same*/ 205 static void post(uint id, EventType type, Gamepad ev) 206 { 207 if(type == EventType.gamepadConnected) 208 { 209 while(controllers.length < id+1) 210 newController(); 211 } 212 import hip.util.format; 213 if(id >= controllers.length) 214 { 215 ErrorHandler.assertExit(false, format!("Input controller out of range!(ID: %s, Type: %s)")(id, type)); 216 } 217 controllers[id].post(cast(ubyte)type, ev); 218 } 219 220 /** Polls an input event for a specified controller */ 221 static InputEvent* poll(uint id) 222 { 223 import hip.util.format; 224 if(id >= controllers.length) 225 { 226 ErrorHandler.assertExit(false, format!("Input controller out of range!(ID: %s)")(id)); 227 } 228 return controllers[id].poll(); 229 } 230 static void clear(uint id) 231 { 232 import hip.util.format; 233 if(id >= controllers.length) 234 { 235 ErrorHandler.assertExit(false, format!("Input controller out of range!(ID: %s)")(id)); 236 } 237 controllers[id].clear(); 238 } 239 alias poll = EventQueue.poll; 240 alias post = EventQueue.post; 241 alias clear = EventQueue.clear; 242 }